package com.aegis.gateway.filter;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.StrUtil;
import com.aegis.common.exception.AuthorizationException;
import com.aegis.common.exception.ServiceException;
import com.aegis.common.web.BaseResponseEnum;
import com.aegis.core.enums.AegisHttpMethod;
import com.aegis.gateway.model.AppInfo;
import com.aegis.gateway.service.cache.CacheService;
import com.aegis.spring.config.AegisAppProperties;
import com.aegis.gateway.constants.FilterChainEnum;
import com.aegis.gateway.enums.PermissionTypeHandlerEnum;
import com.aegis.gateway.strategy.FilterDistributeContext;
import com.aegis.gateway.strategy.FilterDistributeHandler;
import com.aegis.manage.api.dto.SysApiPermissionDTO;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;
import org.springframework.util.PathMatcher;
import org.springframework.web.server.ServerWebExchange;
import org.springframework.web.server.WebFilterChain;
import reactor.core.publisher.Mono;

import java.util.List;
import java.util.Map;

/**
 * @Author wuweixin
 *
 * @Version 1.0
 * @Descritube 分发摘要鉴权请求处理
 */
@Slf4j
@Component
public class DistributeSignatureFilter extends BaseWebFilter {


    @Autowired
    private AegisAppProperties aegisAppProperties;

    @Autowired
    private CacheService<AppInfo> cacheService;

    @Autowired
    private FilterDistributeContext filterDistributeContext;

    private final PathMatcher pathMatcher = new AntPathMatcher();

    @Override
    public Mono<Void> doFilter(ServerWebExchange exchange, WebFilterChain chain) {
        ServerHttpRequest request = exchange.getRequest();
        List<String> appIdList = request.getHeaders().get(aegisAppProperties.getHeaderNameAppId());
        if (CollectionUtil.isEmpty(appIdList)) {
            throw new AuthorizationException(BaseResponseEnum.UNAUTHORIZED, "缺少应用标识");
        }
        String appId = appIdList.get(0);

        Map<String, AppInfo> cacheMap = cacheService.getCache();
        //至少会有aegis管理应用包含在内，若整个都没数据，可能是服务刚启动数据未来得及初始化就有请求打进来，也可能是依赖的其他服务挂了
        if (cacheMap == null || cacheMap.isEmpty()) {
            //todo 可以在服务刚启动，没数据的时候先读redis缓存，后续再读本地缓存
            throw new ServiceException(BaseResponseEnum.UNAVAILABLE, "服务繁忙，请稍后再试");
        }

        AppInfo cache = cacheMap.get(appId);
        if (cache == null) {
            throw new AuthorizationException(BaseResponseEnum.UNAUTHORIZED, "应用未注册");
        }
        filterManageService.setAppInfo(exchange, cache.getAppInfo());

        List<SysApiPermissionDTO> sysApiPermissionDTOS = cache.getPermissions();

        if (CollectionUtil.isEmpty(sysApiPermissionDTOS)) {
            throw new AuthorizationException(BaseResponseEnum.NO_PERMISSION, "当前应用未具备该接口权限");
        }
        // 获取当前请求路径
        String apiUrl = request.getURI().getPath();
        SysApiPermissionDTO currentPermission = null;
        for (SysApiPermissionDTO sysApiPermissionDTO : sysApiPermissionDTOS) {
            if (!pathMatcher.match(sysApiPermissionDTO.getApi(), apiUrl)) {
                continue;
            }
            if (StrUtil.isBlank(sysApiPermissionDTO.getMethod())) {
                continue;
            }
            if (AegisHttpMethod.ALL == AegisHttpMethod.toEnum(sysApiPermissionDTO.getMethod())
                    || request.getMethod().matches(sysApiPermissionDTO.getMethod())) {
                currentPermission = sysApiPermissionDTO;
            }
        }
        //如果当前请求没有匹配到任何权限
        if (currentPermission == null) {
            throw new AuthorizationException(BaseResponseEnum.NO_PERMISSION, "当前应用未具备该接口权限");
        } else {
            //分发请求由哪个过滤器处理
            PermissionTypeHandlerEnum permissionTypeHandlerEnum = PermissionTypeHandlerEnum.getEnum(currentPermission.getType());
            FilterDistributeHandler handler = filterDistributeContext.getHandler(permissionTypeHandlerEnum);
            handler.handle(exchange, currentPermission);
        }

        return chain.filter(exchange);
    }

    @Override
    public int getOrder() {
        return FilterChainEnum.DISTRIBUTE_FILTER.getOrder();
    }
}
